package com.toy.core.security.exception;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.toy.core.config.SysConfig;
import com.toy.core.security.Authentication;
import com.toy.core.security.SecurityRuntimeException;
import com.toy.core.security.access.AccessDeniedException;
import com.toy.core.security.context.SecurityContextHolder;
import com.toy.core.security.login.entrypoint.LoginEntryPoint;
import com.toy.core.security.login.timeout.ExpiredMechanism;
import com.toy.core.security.logout.LogoutException;
import com.toy.core.security.logout.handler.LogoutHandler;
import com.toy.core.util.UrlUtils;

public class ExceptionTranslationFilter implements Filter {
	
	//~ Static fields/initializers =====================================================================================

	private static final Log logger = LogFactory.getLog(ExceptionTranslationFilter.class);
	
	@Autowired
	private SysConfig sysConfig;
	
	//超时重新登录机制
	private ExpiredMechanism expiredMechanism;
	
	//~ Methods ========================================================================================================
	
	public void destroy() {}
	
	public void init(FilterConfig filterConfig) throws ServletException {}

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		if (!(request instanceof HttpServletRequest)) {
			throw new ServletException("HttpServletRequest required");
		}

		if (!(response instanceof HttpServletResponse)) {
			throw new ServletException("HttpServletResponse required");
		}

		try {
			chain.doFilter(request, response);

			if (logger.isDebugEnabled()) {
				logger.debug("Chain processed normally");
			}
		}
		catch (AuthenticationException ex) {
			handleException(request, response, chain, ex);
		}
		catch (LogoutException ex) {
			handleException(request, response, chain, ex);
		}
		catch (AccessDeniedException ex) {
			handleException(request, response, chain, ex);
		}
		catch (ServletException ex) {
			if (ex.getRootCause() instanceof AuthenticationException
					|| ex.getRootCause() instanceof AccessDeniedException) {
				handleException(request, response, chain, (SecurityRuntimeException) ex.getRootCause());
			}
			else {
				throw ex;
			}
		}
		catch (IOException ex) {
			throw ex;
		}

	}

	private void handleException(ServletRequest request, ServletResponse response, FilterChain chain,
			SecurityRuntimeException exception) throws IOException, ServletException {
		HttpServletRequest httprequest = (HttpServletRequest)request;
		HttpServletResponse httpresponse = (HttpServletResponse)response;
		//FIXED:针对不同的错误跳转不同的页面
		//比如，没有登陆时或者登陆超时时，先尝试cookie或者param登录，
		//这是系统内的自动登陆，跟ntlm或者证书自动登陆应该区别看待
		//cookie或者param登录,是维护已登陆用户保证系统运行不出现登陆超时的一个机制
		//ntlm或者证书自动登陆,是从外部认证，用户本身没有登陆的情况进入系统
		//不行的话然后跳转到登陆页面，
		//这里跟logout应该是一样的，所有logoutfilter只是抛错
		//没有权限时，跳转到无权页面
		
		//跳转前都需要进行 logout处理，比如jdbc/cookie/ntlm，跳转前都需要处理
		//必须先处理然后跳转
		if (exception instanceof AuthenticationException) {
			if (logger.isDebugEnabled()) {
				logger.debug("Authentication exception occurred; redirecting to authentication entry point", exception);
			}
			//超时重新登录,或者通过ticket/cookie登陆
			boolean reLoginSuccee = expiredMechanism.run(httprequest, httpresponse);
			if (reLoginSuccee){
				String target = UrlUtils.getFullRequestUrl(httprequest);
				httpresponse.sendRedirect(target);
			}else{
				sendStartAuthentication(httprequest, httpresponse, chain,true);
			}
		}else if (exception instanceof LogoutException){
			if (logger.isDebugEnabled()) {
				logger.debug("LogoutException exception occurred; redirecting to authentication entry point", exception);
			}
			//登出处理
			LogoutHandler[] handlers =  sysConfig.getLogoutChain();
			Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
			for (LogoutHandler logoutHandler : handlers) {
				logoutHandler.logout(httprequest, httpresponse, authentication);
			}
			sendStartAuthentication(httprequest, httpresponse, chain,false);
		}else if (exception instanceof AccessDeniedException) {
			if (logger.isDebugEnabled()) {
				logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler",
						exception);
			}
			//跳转到无权显示页面
			String accessDeniedPage = httprequest.getContextPath() + sysConfig.getAccessDeniedPageUrl();
			httpresponse.sendRedirect(accessDeniedPage);
		}
	}
	
	protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response,
			FilterChain chain,boolean containTarget) 
		throws ServletException, IOException {
		LoginEntryPoint entryPoint =  sysConfig.getLoginEntryPoint();
		entryPoint.commence(request, response, containTarget);
	}

	public void setExpiredMechanism(ExpiredMechanism expiredMechanism) {
		this.expiredMechanism = expiredMechanism;
	}
	
}
